 
// This file was originally written by ZDimitor.

//----------------------------------------------------------------------
//  somefuncs.h -  part of the My3D Tools
//
//  This tool was created by Zhuck Dmitry (ZDimitor).
//  Everyone can use it as wants ( i'll be happy if it helps to someone :) ).
//----------------------------------------------------------------------

//**********************************************************************
//                      some usefull functions
//**********************************************************************

#ifndef __C_MY3D_HELPER_H_INCLUDED__
#define __C_MY3D_HELPER_H_INCLUDED__

//--------------------------------------------------------------------------------
namespace irr 
{
    namespace core
    {

        //---------------------------------------------------------------------
        inline f32 fmax(f32 v1, f32 v2)
        {
            return (v1>v2) ? v1 : v2;
        }
        //----------------------------------------------------------------------------
        template <class T>
        s32 strlen(T* str)
        {
            int len = 0;
            while(true)
            {   if (*(str+len)==0)
            break;
            len++;}
            return len;
        }
        //----------------------------------------------------------------------------
        template <class T>
        T* strcpy (T* src, T* dst)
        {
            if (src&&dst)
            {   s32 len = strlen(src);
            for (s32 i=0; i<len+1; i++)
                dst[i] = src[i];
            return dst;}
            return NULL;
        }
        //----------------------------------------------------------------------------
        template <class T>
        T* strcpy (T* src, T* dst, s32 startPos, s32 endPos)
        {
            if (src&&dst)
            {   s32 len = strlen(src);

            s32 charIndex=0;
            for (s32 i=startPos; i<=endPos && endPos<len; i++)
            {   dst[charIndex] = src[i];
            charIndex++;}
            dst[charIndex]=0;

            return dst;}
            return NULL;
        }
        //----------------------------------------------------------------------------
        template <class T>
        s32 findLast(T* Str, c8 ch)
        {
            s32 lenSrc = strlen(Str);
            s32 n=lenSrc-1;
            s32 lastCharInStringPos = -1;
            while(true)
            {   if (Str[n] == ch)
            {   lastCharInStringPos = n;
            break;}
            if (n==0) break;
            n--;}
            return lastCharInStringPos;
        }
        //----------------------------------------------------------------------------
        template <class T>
        s32 findLastDelimiter(T* Str)
        {
            s32 lenSrc = strlen(Str);
            s32 n=lenSrc-1;
            s32 lastDelimiterInStringPos = -1;
            while(true)
            {   if (Str[n] == '\\' || Str[n] == '/')
            {   lastDelimiterInStringPos = n;
            break;}
            if (n==0) break;
            n--;}
            return lastDelimiterInStringPos;
        }
        //----------------------------------------------------------------------------
        template <class T>
        T* extractFilePath(T* sourceStr, T* buffer, s32 bufSize)
        {
            s32 lastDelimiterInStringPos = findLastDelimiter(sourceStr);
            s32 lenSrc = strlen(sourceStr);

            if (lastDelimiterInStringPos>=0 && lastDelimiterInStringPos<bufSize)
                strcpy(sourceStr, buffer, 0, lastDelimiterInStringPos);

            return buffer;
        }
        //----------------------------------------------------------------------------
        template <class T>
        T* extractFileName(T* sourceStr, T* buffer, s32 bufSize)
        {
            s32 lastDelimiterInStringPos = findLastDelimiter(sourceStr);
            s32 lenSrc = strlen(sourceStr);

            if (lastDelimiterInStringPos>=-1 && (lenSrc-lastDelimiterInStringPos)<bufSize)
                strcpy(sourceStr, buffer, lastDelimiterInStringPos+1, lenSrc-1);

            return buffer;
        }

        //-----------------RLE stuff-----------------------------------------

        int rle_encode (
            unsigned char *in_buf,  int in_buf_size,
            unsigned char *out_buf, int out_buf_size
            );
        int process_comp(
            unsigned char *buf, int buf_size,
            unsigned char *out_buf, int out_buf_size
            );
        void process_uncomp(
            unsigned char, unsigned char *out_buf, int out_buf_size
            );
        void flush_outbuf(
            unsigned char *out_buf, int out_buf_size
            );
        int get_byte (
            unsigned char *ch,
            unsigned char *in_buf, int in_buf_size,
            unsigned char *out_buf, int out_buf_size
            );
        void put_byte(
            unsigned char ch, unsigned char *out_buf, int out_buf_size
            );
        //-----------------------------------------------------------
        const unsigned long LIMIT = 1; // was #define LIMIT     1
        const unsigned long NON_MATCH = 2; // was: #define NON_MATCH 2
        const unsigned long EOD_FOUND = 3; // was: #define EOD_FOUND 3
        const unsigned long EOD = 0x00454f44; // was: #define EOD       'EOD'
        //-----------------------------------------------------------
        // number of decoded bytes
        int nDecodedBytes=0;
        // number of coded bytes
        int nCodedBytes=0;
        // number of readed bytes
        int nReadedBytes=0;
        // table used to look for sequences of repeating bytes
        unsigned char tmpbuf[4];  // we use subscripts 1 - 3
        int tmpbuf_cnt;
        // output buffer for non-compressed output data
        unsigned char outbuf[128];
        int outbuf_cnt;


        //-----------------------------------------------------------
        int rle_encode (
            unsigned char *in_buf,  int in_buf_size,
            unsigned char *out_buf, int out_buf_size
            )
        {
            int ret_code;

            unsigned char ch;

            nCodedBytes=0;
            nReadedBytes=0;

            tmpbuf_cnt = 0; // no. of char's in tmpbuf
            outbuf_cnt = 0; // no. of char's in outbuf
            while (1)
            {
                if (get_byte(&ch, in_buf, in_buf_size,
                    out_buf, out_buf_size) == (int)EOD)  // read next byte into ch
                    break;

                tmpbuf[++tmpbuf_cnt] = (unsigned char) ch;
                if (tmpbuf_cnt == 3)
                {
                    // see if all 3 match each other
                    if ((tmpbuf[1] == tmpbuf[2]) && (tmpbuf[2] == tmpbuf[3]))
                    {
                        // they do - add compression
                        // this will process all bytes in input file until
                        // a non-match occurs, or 128 bytes are processed,
                        // or we find eod */
                        ret_code = process_comp(in_buf, in_buf_size, out_buf, out_buf_size);
                        if (ret_code == (int)EOD_FOUND)
                            break; // stop compressing
                        if (ret_code == (int)NON_MATCH)
                            tmpbuf_cnt=1; /* save the char that didn't match */
                        else
                            // we just compressed the max. of 128 bytes
                            tmpbuf_cnt=0; /* start over for next chunk */
                    }
                    else
                    {
                        // we know the first byte doesn't match 2 or more
                        //  others, so just send it out as uncompressed. */
                        process_uncomp(tmpbuf[1], out_buf, out_buf_size);

                        // see if the last 2 bytes in the buffer match
                        if (tmpbuf[2] == tmpbuf[3])
                        {
                            // move byte 3 to position 1 and pretend we just
                            // have 2 bytes -- note that the first byte was
                            // already sent to output */
                            tmpbuf[1]=tmpbuf[3];
                            tmpbuf_cnt=2;
                        }
                        else
                        {
                            // send byte 2 and keep byte 3 - it may match the
                            // next byte.  Move byte 3 to position 1 and set
                            // count to 1.  Note that the first byte was
                            // already sent to output
                            process_uncomp(tmpbuf[2], out_buf, out_buf_size);
                            tmpbuf[1]=tmpbuf[3];
                            tmpbuf_cnt=1;
                        }
                    }
                }
            } // end while
            flush_outbuf(out_buf, out_buf_size);

            return nCodedBytes;
        }


        //------------------------------------------------------------------
        // This flushes any non-compressed data not yet sent, then it processes
        // repeating bytes until > 128, or EOD, or non-match.
        //      return values: LIMIT, EOD_FOUND, NON_MATCH
        // Prior to ANY return, it writes out the 2 byte compressed code.
        // If a NON_MATCH was found, this returns with the non-matching char
        // residing in tmpbuf[0].
        //      Inputs: tmpbuf[0], input file
        //      Outputs: tmpbuf[0] (sometimes), output file, and return code
        //------------------------------------------------------------------
        int process_comp(
            unsigned char *buf, int buf_size,
            unsigned char *out_buf, int out_buf_size)
        {
            // we start out with 3 repeating bytes
            register int len = 3;

            unsigned char ch;

            // we're starting a repeating chunk - end the non-repeaters
            flush_outbuf(out_buf, out_buf_size);


            while (get_byte(&ch, buf, buf_size, out_buf, out_buf_size) != (int)EOD)
            {
                if (ch != tmpbuf[1])
                {
                    // send no. of repeated bytes to be encoded
                    put_byte((unsigned char)((--len) | 0x80), out_buf, out_buf_size);
                    // send the byte's value being repeated
                    put_byte((unsigned char)tmpbuf[1], out_buf, out_buf_size);
                    /* save the non-matching character just read */
                    tmpbuf[1]=(unsigned char) ch;
                    return NON_MATCH;
                }
                /* we know the new byte is part of the repeating seq */
                len++;
                if (len == 128)
                {
                    // send no. of repeated bytes to be encoded
                    put_byte((unsigned char)((--len) | 0x80), out_buf, out_buf_size);
                    // send the byte's value being repeated
                    put_byte((unsigned char)tmpbuf[1], out_buf, out_buf_size);
                    return LIMIT;
                }
            } // end while

            // if flow comes here, we just read an EOD
            // send no. of repeated bytes to be encoded
            put_byte((unsigned char)((--len) | 0x80), out_buf, out_buf_size);
            // send the byte's value being repeated
            put_byte((unsigned char)tmpbuf[1], out_buf, out_buf_size);
            return EOD_FOUND;
        }


        //----------------------------------------------------------------
        // This adds 1 non-repeating byte to outbuf.  If outbuf becomes full
        // with 128 bytes, it flushes outbuf.
        // There are no return codes and no bytes are read from the input.
        //----------------------------------------------------------------
        void process_uncomp(
            unsigned char char1, unsigned char *out_buf, int out_buf_size
            )
        {
            outbuf[outbuf_cnt++] = char1;
            if (outbuf_cnt == 128)
                flush_outbuf(out_buf, out_buf_size);
        }
        //-----------------------------------------------------------
        // This flushes any non-compressed data not yet sent.
        // On exit, outbuf_cnt will equal zero.
        //-----------------------------------------------------------
        void flush_outbuf(unsigned char *out_buf, int out_buf_size)
        {
            register int pos=0;

            if(!outbuf_cnt)
                return; // nothing to do */

            // send no. of unencoded bytes to be sent
            put_byte((unsigned char)(outbuf_cnt - 1), out_buf, out_buf_size);

            for ( ; outbuf_cnt; outbuf_cnt--)
                put_byte((unsigned char)outbuf[pos++], out_buf, out_buf_size);
        }
        //---------------------------------------------------
        void put_byte(unsigned char ch, unsigned char *out_buf, int out_buf_size)
        {
            if (nCodedBytes<=(out_buf_size-1))
            {   out_buf[nCodedBytes++]=ch;
            out_buf[nCodedBytes]=0;}
        }
        //---------------------------------------------------
        // This reads the next byte into ch.  It returns EOD
        // at end-of-data
        //---------------------------------------------------
        int get_byte(
            unsigned char *ch,
            unsigned char *in_buf, int in_buf_size,
            unsigned char *out_buf, int out_buf_size
            )
        {
            if (nReadedBytes>=in_buf_size)
            {
                // there are either 0, 1, or 2 char's to write before we quit
                if (tmpbuf_cnt == 1)
                    process_uncomp(tmpbuf[1], out_buf, out_buf_size);
                else
                {
                    if (tmpbuf_cnt == 2)
                    {
                        process_uncomp(tmpbuf[1], out_buf, out_buf_size);
                        process_uncomp(tmpbuf[2], out_buf, out_buf_size);
                    }
                }
                nReadedBytes =0;

                return EOD;
            }

            (*ch) = (unsigned char)in_buf[nReadedBytes++];

            return 0;
        }
        //-----------------------------------------------------------
        int rle_decode (
            unsigned char *in_buf,  int in_buf_size,
            unsigned char *out_buf, int out_buf_size
            )
        {
            nDecodedBytes=0;
            nReadedBytes=0;

            int ch, i;
            while (1)
            {

                if (nReadedBytes>=in_buf_size)
                    break;
                else
                    ch=in_buf[nReadedBytes];
                nReadedBytes++;

                if (ch > 127)
                {
                    i = ch - 127; // i is the number of repetitions
                    // get the byte to be repeated
                    if (nReadedBytes>=in_buf_size)
                        break;
                    else
                        ch=in_buf[nReadedBytes];
                    nReadedBytes++;

                    // uncompress a chunk
                    for ( ; i ; i--)
                    {
                        if (nDecodedBytes<out_buf_size)
                            out_buf[nDecodedBytes] = ch;
                        nDecodedBytes++;
                    }
                }
                else
                {
                    // copy out some uncompressed bytes
                    i = ch + 1; // i is the no. of bytes
                    // uncompress a chunk
                    for ( ; i ; i--)
                    {
                        if (nReadedBytes>=in_buf_size)
                            break;
                        else
                            ch=in_buf[nReadedBytes];
                        nReadedBytes++;

                        if (nDecodedBytes<out_buf_size)
                            out_buf[nDecodedBytes] = ch;
                        nDecodedBytes++;
                    }
                }
            } // end while

            return nDecodedBytes;
        }

    } //end namespace core
} //end namespace irr


#endif // __C_MY3D_HELPER_H_INCLUDED__

